/*
* Copyright (c) 2008-2011, Thomas Handorf
* 
* This file is part of libScopes.
* libScopes is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* 
* libScopes is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU Lesser General Public License
* along with libScopes.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "net.h"
#include "sbml.h"
#include "scopes.h"
#include <stdio.h>
#include <unistd.h>
#include "tokenizer.h"

struct config{
   array<int> times;
   array<VC*> seeds;
   array<VC*> blocks;
   VS report;
   bool combined;
};
config loadConfig(const char* filename,Net& net);
void printConfig(Net net,config cfg);
void run(Net net,config cfg);

int main(int argc, char* argv[]){
/*   char cCurrentPath[FILENAME_MAX];
   getcwd(cCurrentPath, sizeof(cCurrentPath));
   printf ("The current working directory is %s\n", cCurrentPath);*/
   if (argc<2) {
      cout << "no experiment file given" << endl;
      abort();
   }
   Net net;
   config cfg=loadConfig(argv[1],net);
   cout << "network extended stoichiometric matrix" << endl;
   net.print_matrix();
   printConfig(net,cfg);
   run(net,cfg);
   return 0;
}
void run(Net net,config cfg){
   size_t i,j,mn,mx,ntimes=cfg.times.size();
   cout << "running experiment" << endl;
   StateList states;
   Channels* chn=new Channels(&net);
   for (i=0;i<ntimes;i++){ // run experiement and store in states
      if (i<ntimes-1) {
         mx=mn=cfg.times[i+1]-cfg.times[i];
      } else {
         mn=0;
         mx=10000; //some reasonably high value
      }
      dScopeRun(&net,mn,mx,&states,cfg.seeds[i],cfg.blocks[i],chn); 
   }
   for (i=0;i<states.size();i++){
      cout << i << ": ";
      VC* sd=states[i]->present->copy();      
      if (i>0) sd->XOR(*states[i-1]->present);
      VC* dd=states[i]->depleted->copy();
      if (i>0) dd->XOR(*states[i-1]->depleted);
      for (j=0;j<net.numC();j++){
         if ((*sd)[j] || (*dd)[j]){
            if ((*states[i]->depleted)[j]) cout << "!";
            cout << (((*states[i]->present)[j]) ? "(1)" : "(0)");
            cout << net.name(j) << " ";
         }
      }
      cout << endl;
   }
   cout << "---begin report---" << endl;
   cout << "time" << " ";
   size_t rsize=cfg.report.size();
   for (j=0;j<rsize;j++){
      cout << net.name(cfg.report[j]);
      if (j!=rsize-1) cout << " ";
   }
   cout << endl;
   for (i=0;i<states.size();i++){
      cout << i << " ";
      for (j=0;j<rsize;j++){
         size_t idx=cfg.report[j];
         if ((*states[i]->present)[idx]){
            cout << ((*states[i]->depleted)[idx] ? (cfg.combined ? "0" :"-1") : "1");
         } else {
            cout << "0";
         }
         if (j!=rsize-1) cout << " ";
      }
      cout << endl;
   }
}
config loadConfig(const char* filename,Net& net){
   cout << "importing experiment" << endl;
   FILE* f=fopen(filename,"r");
   if (!f){
      printf("could not find experiment file %s\n",filename);
      abort();
   }
   Tokenizer tk(f);
   char sbml[255],file[255];
   char s[255],dummy[255];
   char mode[2];
   int time,ret=1;
   int ntimes=0;
   bool all=false;
   config cfg;
   while (tk.matchstart("#")){// remove comments
      tk.skipline();
   }
   if (!tk.match(":network")) {cout << "expected :network directive" <<endl; abort();}
   tk.get(sbml);
   nodemap nm=importSBML(net,sbml);
   if (tk.match(":inhibitions")){
      tk.get(file);
      importInhibition(net,file,nm);
   }
   if (tk.match(":members")){
      tk.get(file);
      importMembers(net,file,nm);
   }
   if (tk.match(":channels")){
      tk.get(file);
      importChannels(net,file,nm);
   }
   nodemap::iterator it;
   for ( it=nm.begin() ; it != nm.end(); it++ ) cout << (*it).first << " => " << (*it).second << endl;
   
   while (1){ 
      while (tk.matchstart("#")){// remove comments
         tk.skipline();
      }
      if (tk.scan(":%d",&time)){ // time directive
         ntimes++;
         if (ntimes>1 && cfg.times[ntimes-2]>=time){
            cout << "time points need to be in chronological order and not the same" << endl;
            abort();
         }
         cfg.times.push_back(time);
         if (tk.match("add") && ntimes>1){ // add to previously defined seed and block
            cfg.seeds.push_back(new VC(*cfg.seeds[ntimes-2])); // copy
            cfg.blocks.push_back(new VC(*cfg.blocks[ntimes-2])); //copy
         } else {
            cfg.seeds.push_back(new VC(net.numC(),(unsigned char) 0));
            cfg.blocks.push_back(new VC(net.numC(),(unsigned char) 0));
         }
      } else { // ? seed directive
         if (!tk.scan("%[+-]",mode)) break; // end of time definitions
         tk.get(s);
         tk.skipline();
         if (!nm.count(string(s))){
            cout << " id " << s << " not in network" << endl;
            abort();
         }
         size_t idx=nm[string(s)];
         if (strcmp(mode,"+")==0){
            (*cfg.seeds[ntimes-1])[idx]=1;
            (*cfg.blocks[ntimes-1])[idx]=0;
         } else {
            (*cfg.blocks[ntimes-1])[idx]=1;
            (*cfg.seeds[ntimes-1])[idx]=0;
         }
      }
   }
   if (ntimes==0){
      cout << "expected time point definitions" << endl;
      abort();
   }
   while (tk.matchstart("#")){ // remove comments
      tk.skipline();
   }
//   fscanf(f," :"); // remove ':' if still present; ':' may have been eaten already by previous scanf on ":time"
   ret=fscanf(f,"repor%[t:]",dummy); // 
   if (!tk.match(":report")){
      cout << "expected :report section" << endl;
      abort();
   }
   all=tk.match("all");
   cfg.combined=tk.match("combined");
   if (!all) all=tk.match("all"); // in case "all" comes after "combined"
   //tk.skipline();
   if (all){ // report all compounds
      size_t i;
      for (i=0;i<net.numC();i++){
         cfg.report.push_back(i);
      }
   } else {
      while (1){
         while (tk.matchstart("#")){ // remove comments
            tk.skipline();
         }
         if (!tk.get(s)) break;
         tk.skipline();
         if (!nm.count(string(s))){
            cout << "section report: id " << s << " not in network" <<endl;
            abort();
         }
         cfg.report.push_back(nm[string(s)]);
      }
   }
   return cfg;
}
void printConfig(Net net,config cfg){
   size_t i;
   cout << "experiment configuration" << endl;
   for (i=0;i<cfg.times.size();i++){
      cout << "at " << cfg.times[i] << endl;
      cout << "seed : " << cfg.seeds[i]->printToString() << endl;
      cout << "block: " << cfg.blocks[i]->printToString() << endl;
   }
   cout << "report:";
   if (cfg.combined) cout << " combined";
   cout << endl;
   for (i=0;i<cfg.report.size();i++){
      cout << cfg.report[i] << " " ;
   }
   cout << endl;
}